package encoding_protocol

import (
	"bytes"
	"encoding/binary"
	"errors"
)

var (
	separator     byte = ';'
	metaSeparator byte = ':'
)

type Request struct {
	headerLength uint32
	bodyLength   uint32
	MessageId    uint32
	Version      uint8
	Compressor   uint8
	Serializer   uint8

	ServiceName string
	MethodName  string
	Meta        map[string]string

	Data []byte
}

func (r *Request) calculateHeaderLength() {
	length := 15                                         // 定长15
	length += len(r.ServiceName) + len(r.MethodName) + 2 // 2是两个分隔符的长度
	for key, value := range r.Meta {
		length += len(key) + len(value) + 2 // 2是分隔符的长度
	}
	r.headerLength = uint32(length)
}

func (r *Request) Encode() []byte {
	r.calculateHeaderLength()
	r.bodyLength = uint32(len(r.Data))
	bs := make([]byte, r.headerLength+r.bodyLength)
	binary.BigEndian.PutUint32(bs[0:4], r.headerLength)
	binary.BigEndian.PutUint32(bs[4:8], r.bodyLength)
	binary.BigEndian.PutUint32(bs[8:12], r.MessageId)
	bs[12] = r.Version
	bs[13] = r.Compressor
	bs[14] = r.Serializer

	val := bs[15:]
	copy(val[0:len(r.ServiceName)], r.ServiceName)
	val[len(r.ServiceName)] = separator
	val = val[len(r.ServiceName)+1:]
	copy(val[0:len(r.MethodName)], r.MethodName)
	val[len(r.MethodName)] = separator
	if len(r.Meta) > 0 {
		val = val[len(r.MethodName)+1:]
		for key, value := range r.Meta {
			copy(val[0:len(key)], key)
			val[len(key)] = metaSeparator
			val = val[len(key)+1:]
			copy(val[0:len(value)], value)
			val[len(value)] = separator
			val = val[len(value)+1:]
		}
		if r.bodyLength > 0 {
			copy(val[0:], r.Data)
		}
	} else {
		if r.bodyLength > 0 {
			val = val[len(r.MethodName)+1:]
			copy(val[0:], r.Data)
		}

	}

	return bs
}

func (r *Request) Decode(b []byte) error {
	r.headerLength = binary.BigEndian.Uint32(b[0:4])
	r.bodyLength = binary.BigEndian.Uint32(b[4:8])
	r.MessageId = binary.BigEndian.Uint32(b[8:12])
	r.Version = b[12]
	r.Compressor = b[13]
	r.Serializer = b[14]

	sliceBs := b[15:]
	idx := bytes.IndexByte(sliceBs, separator)
	if idx < 0 {
		return errors.New("invalid service name")
	}

	r.ServiceName = string(sliceBs[:idx])
	sliceBs = sliceBs[idx+1:]
	idx = bytes.IndexByte(sliceBs, separator)
	if idx < 0 {
		return errors.New("invalid method name")
	}

	r.MethodName = string(sliceBs[:idx])

	sliceBs = sliceBs[idx+1:]
	idx = bytes.IndexByte(sliceBs, separator)
	meta := make(map[string]string, 16)
	for {
		if idx < 0 { // 没有meta部分
			break
		}
		jdx := bytes.IndexByte(sliceBs[:idx], metaSeparator)
		if jdx < 0 {
			return errors.New("invalid metadata")
		}
		key := string(sliceBs[:jdx])
		value := string(sliceBs[jdx+1 : idx])
		meta[key] = value
		sliceBs = sliceBs[idx+1:]
		idx = bytes.IndexByte(sliceBs, separator)
	}
	r.Meta = meta
	r.Data = sliceBs[0:]

	return nil
}
